package com.seelyn.rhea.mvc.internal.matcher;

import java.io.Serializable;
import java.util.Objects;

/**
 * @author linfeng
 * @since 2016/1/20
 */
public abstract class AbstractMatcher<T> implements OperationMatcher<T> {

    public Matcher<T> and(Matcher<? super T> other) {
        return new AndMatcher<>(this, other);
    }

    public Matcher<T> or(Matcher<? super T> other) {
        return new OrMatcher<>(this, other);
    }

    /**
     * @author linfeng
     * @since 2016/1/20
     */
    private static class AndMatcher<E> extends AbstractMatcher<E> implements Serializable {
        private static final long serialVersionUID = -1861330849088345507L;
        private final Matcher<? super E> one, two;

        public AndMatcher(Matcher<? super E> one, Matcher<? super E> two) {
            this.one = one;
            this.two = two;
        }

        public boolean matching(E e) {
            return one.matching(e) && two.matching(e);
        }

        @Override
        public boolean equals(Object o) {
            if (o == this) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }
            if (!(o instanceof AndMatcher)) {
                return false;
            }
            @SuppressWarnings("unchecked")
            AndMatcher<?> that = (AndMatcher<?>) o;
            return Objects.equals(one, that.one) &&
                    Objects.equals(two, that.two);
        }

        @Override
        public int hashCode() {
            return Objects.hash(one, two);
        }

        @Override
        public String toString() {
            return "AndMatcher{" +
                    "one=" + one +
                    ", two=" + two +
                    '}';
        }
    }

    /**
     * @author linfeng
     * @since 2016/1/20
     */
    private static class OrMatcher<D> extends AbstractMatcher<D> implements Serializable {
        private static final long serialVersionUID = 176733431188728792L;
        private final Matcher<? super D> one,two;

        public OrMatcher(Matcher<? super D> one, Matcher<? super D> two) {
            this.one = one;
            this.two = two;
        }

        public boolean matching(D d) {
            return one.matching(d) || two.matching(d);
        }

        @SuppressWarnings("unchecked")
        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }
            OrMatcher<?> orMatcher = (OrMatcher<?>) o;
            return Objects.equals(one, orMatcher.one) &&
                    Objects.equals(two, orMatcher.two);
        }

        @Override
        public int hashCode() {
            return Objects.hash(one, two);
        }

        @Override
        public String toString() {
            return "OrMatcher{" +
                    "one=" + one +
                    ", two=" + two +
                    '}';
        }
    }


}
